import dayjs from 'dayjs'
import 'dayjs/plugin/lunar' // 农历插件
declare module 'dayjs' {
    interface Dayjs {
        lunar: () => {
            year: number
            month: number
            day: number
            isLeap: boolean
        }
    }
}

/**
 * 将数字格式化为两位显示
 * @param n - 需要格式化的数字
 * @returns 返回格式化后的字符串，如果是一位数则前面补0
 */
export function formatNumber(n: any): string {
    const nChars = n.toString()
    return nChars.length !== 1 ? nChars : '0' + nChars
}

/**
 * 获取月日 星期
 */
/**
 * 获取当前日期时间字符串
 * @returns 返回格式化的日期字符串，包含月日和周几
 */
export function getDateTimeString(): string {
    const date = new Date()
    const month = formatNumber(date.getMonth() + 1)
    const day = formatNumber(date.getDate())
    const week = date.getDay()
    const weekStr = getWeekString(week)
    return `${month}月${day}日   ${weekStr}`
}

/**
 * 根据索引获取星期字符串
 * @param index - 星期索引（0-6）
 * @returns 返回对应的星期字符串
 */
export function getWeekString(index: number): string {
    const weekArr = ['日', '一', '二', '三', '四', '五', '六']
    let weekStr = '星期一'
    if (index < weekArr.length) {
        weekStr = weekArr[index]
    }
    return `星期${weekStr}`
}

/**
 * 格式化日期字符串并判断是否在本周
 * @param dateStr - 日期字符串、时间戳或Date对象
 * @param format - 日期格式，默认为'YYYY/MM/DD'
 * @returns 如果日期在本周则返回星期几，否则返回格式化日期
 */
export function weekformatDateStr(dateStr: string | number | Date, format = 'YYYY/MM/DD'): string {
    return weekformatDate(new Date(dateStr), format)
}

/**
 * 格式化日期并判断是否在本周
 * @param date - Date对象
 * @param format - 日期格式，默认为'YYYY/MM/DD'
 * @returns 如果日期在本周则返回星期几，否则返回格式化日期
 */
export function weekformatDate(date: Date, format = 'YYYY/MM/DD'): string {
    const today = new Date()

    const startOfThisWeek = new Date(today.getFullYear(), today.getMonth(), today.getDate() - today.getDay() + 1)
    const endOfThisWeek = new Date(today.getFullYear(), today.getMonth(), today.getDate() + (6 - today.getDay()) + 1)

    if (date >= startOfThisWeek && date <= endOfThisWeek) {
        const week = date.getDay()
        return getWeekString(week)
    } else {
        return dayjs(date).format(format)
    }
}
/**
 * 获取当前时间并格式化输出
 * @param options - 配置项
 * @param options.format - 时间格式，默认为'HH:mm'
 * @returns 返回格式化后的时间字符串
 */
export function getNowTimeStringWithFormat({ format = 'HH:mm' }: { format?: string }): string {
    const timeStr = dayjs().format(format)
    return timeStr
}

/**
 * 获取当前时间戳
 * @returns 返回当前时间的时间戳（毫秒）
 */
export function getNowTime(): number {
    return dayjs().unix() * 1000
}

/**
 * 将公历日期转换为农历日期
 * @param date - 公历日期对象
 * @param format - 输出格式（支持YYYY、MM、DD占位符）
 * @returns 返回格式化后的农历日期字符串
 */
export function formatLunarDate(date: Date, format = 'YYYY年MM月DD日'): string {
    const lunar = dayjs(date).lunar()
    return format
        .replace('YYYY', `${lunar.year}`)
        .replace('MM', formatNumber(lunar.month))
        .replace('DD', formatNumber(lunar.day))
}

/** 节假日配置类型 */
/**
 * 节假日配置类型
 * @property date - 日期字符串
 * @property name - 节假日名称
 * @property isOffDay - 是否为休息日
 * @property adjustDay - 调休日（可选）
 */
export interface HolidayConfig {
    date: string
    name: string
    isOffDay: boolean
    adjustDay?: string
}

/** 2023年法定节假日配置示例 */
const holidayConfig: HolidayConfig[] = [
    { date: '2023-10-01', name: '国庆节', isOffDay: true },
    { date: '2023-09-29', name: '中秋节', isOffDay: true },
    { date: '2023-01-22', name: '春节', isOffDay: true, adjustDay: '2023-01-28' }
]

/**
 * 判断是否是工作日
 * @param date - 要判断的日期
 * @param includeAdjust - 是否考虑调休日，默认为true
 * @returns 如果是工作日返回true，否则返回false
 */
export function isWorkday(date: Date, includeAdjust = true): boolean {
    const dateStr = dayjs(date).format('YYYY-MM-DD')

    // 检查周末
    const isWeekend = [0, 6].includes(date.getDay())

    // 检查节假日配置
    const holiday = holidayConfig.find(c => c.date === dateStr)
    if (holiday?.isOffDay) return false

    // 检查调休日
    if (includeAdjust) {
        const adjust = holidayConfig.find(c => c.adjustDay === dateStr)
        if (adjust) return true
    }

    return !isWeekend
}

/**
 * 计算工作日间隔
 * @param start 开始日期
 * @param end 结束日期（默认当前时间）
 */
/**
 * 计算两个日期之间的工作日天数
 * @param start - 开始日期
 * @param end - 结束日期，默认为当前时间
 * @returns 返回两个日期之间的工作日天数
 */
export function workdayDiff(start: Date, end: Date = new Date()): number {
    let count = 0
    const current = new Date(start)

    while (current <= end) {
        if (isWorkday(current)) count++
        current.setDate(current.getDate() + 1)
    }

    return count
}

/**
 * 将时间差（毫秒）转换为不同的时间单位
 *
 * @param DiffT 时间差（毫秒）
 * @returns 包含不同时间单位（毫秒、秒、分钟、小时、天、年）的对象
 */
/**
 * 时间差对象类型定义
 */
/**
 * 时间差对象类型定义
 * @property ms - 毫秒
 * @property s - 秒
 * @property m - 分钟
 * @property h - 小时
 * @property d - 天
 * @property y - 年
 * @property all - 包含所有时间单位的数组
 * @property humanize - 返回人类可读的时间差描述
 */
export interface TimeDifference {
    ms: number
    s: number
    m: number
    h: number
    d: number
    y: number
    all: number[]
    humanize: () => string
}

/**
 * 生成日期范围
 * @param start 开始日期
 * @param end 结束日期
 * @param interval 间隔天数
 */
/**
 * 生成日期范围
 * @param start - 开始日期
 * @param end - 结束日期
 * @param interval - 间隔天数，默认为1
 * @returns 返回生成的日期数组
 */
export function dateRangeGenerator(start: Date, end: Date, interval = 1): Date[] {
    const dates: Date[] = []
    const current = new Date(start)

    while (current <= end) {
        dates.push(new Date(current))
        current.setDate(current.getDate() + interval)
    }

    return dates
}

/**
 * 将时间差（毫秒）转换为不同的时间单位
 * @param DiffT - 时间差（毫秒）
 * @returns 返回包含不同时间单位的TimeDifference对象
 */
export const timeDifference = (DiffT: number): TimeDifference => {
    const Diff: any = {
        ms: DiffT,
        s: Math.trunc(DiffT / 1000),
        m: Math.trunc(DiffT / 60000),
        h: Math.trunc(DiffT / 3600000),
        d: Math.trunc(DiffT / 86400000),
        y: Math.trunc(DiffT / 31536000000),
        all: [0, 0, 0, 0, 0, 0]
    }
    DiffT = DiffT / 1000
    if (DiffT < 0) {
        DiffT *= -1
        Diff.all[0] = 1
    }
    Diff.all[1] = Math.trunc(DiffT / 31536000)
    if (Diff.all[1]) DiffT -= Diff.all[1] * 31536000
    Diff.all[2] = Math.trunc(DiffT / 86400)
    if (Diff.all[2]) DiffT -= Diff.all[2] * 86400
    Diff.all[3] = Math.trunc(DiffT / 3600)
    if (Diff.all[3]) DiffT -= Diff.all[3] * 3600
    Diff.all[4] = Math.trunc(DiffT / 60)
    if (Diff.all[4]) DiffT -= Diff.all[4] * 60
    Diff.all[5] = Math.trunc(DiffT)
    Diff.humanize = () => {
        if (Diff.y > 0) return `${Diff.y}年前`
        if (Diff.d > 0) return `${Diff.d}天前`
        if (Diff.h > 0) return `${Diff.h}小时前`
        if (Diff.m > 0) return `${Diff.m}分钟前`
        return '刚刚'
    }
    return Diff
}
/**
 * 计算两个时间点之间的差值
 *
 * @param start 开始时间，可以是 Date 对象、字符串或数字（表示时间戳）
 * @param end 结束时间，可以为 Date 对象、字符串、数字（表示时间戳）或 null（默认为 null）
 * @returns 返回两个时间点之间的差值（毫秒）
 */
/**
 * 计算两个时间点之间的差值
 * @param start - 开始时间，可以是Date对象、字符串或时间戳
 * @param end - 结束时间，默认为null（当前时间）
 * @returns 返回两个时间点之间的差值（毫秒）
 */
export const timeDiff = (start: Date | string | number, end: Date | null | string | number = null) => {
    const D1: Date = new Date(start)
    const D2: Date = end ? new Date(end) : new Date()
    const DiffT = Number(D2) - Number(D1)
    return timeDifference(DiffT)
}
